using Admin.Util;
using Coldairarrow.Util;
using Furion.DependencyInjection;
using Furion.FriendlyException;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Caching.Memory;
using Newtonsoft.Json;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Admin.Core
{
    /// <summary>
    /// 用户管理接口
    /// </summary>
    public class Operator : IOperator, IScoped
    {
        /// <summary>
        /// 用户仓储
        /// </summary>
        private readonly ISqlSugarRepository<BaseUser> repository;
        private readonly ISqlSugarClient db;

        /// <summary>
        /// HttpContext 访问器
        /// </summary>
        private readonly IHttpContextAccessor _httpContextAccessor;

        /// <summary>
        /// 缓存
        /// </summary>
        private readonly IMemoryCache _memoryCache;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="httpContextAccessor">HttpContext访问</param>
        /// <param name="memoryCache">缓存</param>
        /// <param name="dbRepository">数据库操作</param>
        public Operator(IHttpContextAccessor httpContextAccessor
            , IMemoryCache memoryCache
            , ISqlSugarRepository<BaseUser> dbRepository)
        {
            _httpContextAccessor = httpContextAccessor;
            _memoryCache = memoryCache;
            repository = dbRepository;
            db = repository.Context;
        }

        /// <summary>
        /// 获取用户 Id
        /// </summary>
        public string UserId => _httpContextAccessor.HttpContext.User.FindFirst("UserId")?.Value;

        /// <summary>
        /// 获取用户信息
        /// </summary>
        public BaseUser User { get => repository.FirstOrDefault(x => x.Id == UserId); }

        /// <summary>
        /// 检查用户是否有效
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        public async Task<BaseUser> CheckUserAsync(string userId)
        {
            // 查询用户是否存在
            var user = await repository.FirstOrDefaultAsync(u => u.Id == userId);
            return user ?? throw Oops.Oh(SystemErrorCodes.u1002);
        }

        /// <summary>
        /// 查询用户角色
        /// </summary>
        /// <param name="userId">用户Id</param>
        /// <returns></returns>
        public async Task<List<BaseRole>> GetRolesAsync(string userId)
        {
            return await _memoryCache.GetOrCreateAsync($"{userId}_roles", async (_) =>
            {
                string sql = "SELECT br.* from Base_User_Role bur, Base_Role br WHERE bur.RoleId=br.Id and bur.UserId = @userId";
                var par = new List<SugarParameter>() { new SugarParameter("@userId", userId) };

                return await db.Ado.SqlQueryAsync<BaseRole>(sql, par);
            });
        }

        /// <summary>
        /// 查询用户权限
        /// </summary>
        /// <param name="userId">用户Id</param>
        /// <returns></returns>
        public async Task<List<BaseSecurity>> GetSecuritiesAsync(string userId)
        {
            var roleList = await GetRolesAsync(userId);
            var roleIds = roleList.Select(x => x.Id).ToArray();

            bool isSystemManage = roleList.Count(x => x.IsSuper == true) > 0;

            return await _memoryCache.GetOrCreateAsync($"{userId}_securities", async (_) =>
            {
                if (isSystemManage) //超管返回所有权限
                {
                    return await db.Queryable<BaseSecurity>().OrderBy(x => x.MenuSort).ToListAsync();
                }
                else //否则查询对应权限列表
                {
                    string sql = "SELECT bs.* FROM Base_Security bs, Base_Role_Security brs WHERE bs.Id = brs.SecurityId AND brs.RoleId IN (@roleIds) UNION ALL SELECT * FROM Base_Security WHERE NeedAction = 0 GROUP BY id ORDER BY MenuSort ASC";

                    return await db.Ado.SqlQueryAsync<BaseSecurity>(sql, new { roleIds });
                }
            });
        }


        #region 保存日志
        /// <summary>
        /// 记录操作日志
        /// </summary>
        /// <param name="msg">内容</param>
        public async Task WriteUserLog(string msg)
        {
            await db.Insertable(InitBaseLog(msg)).ExecuteCommandAsync();
        }

        /// <summary>
        /// 记录操作日志
        /// </summary>
        /// <returns></returns>
        public async Task WriteUserLog(ActionExecutingContext context,
            ActionExecutionDelegate next)
        {
            var log = InitBaseLog("");
            if (log.RequestMethod == "GET")
            {
                log.ExecuteParam = JsonConvert.SerializeObject(context.HttpContext.Request.QueryString.Value);
            }
            else
            {
                if (context.ActionArguments.Count > 0)
                {
                    log.OperUrl += context.HttpContext.Request.QueryString.Value.ParseToString();
                    log.ExecuteParam = context.ActionArguments.ToIDictionaryString().RemoveTabs();
                }
                else
                {
                    log.ExecuteParam = JsonConvert.SerializeObject(context.HttpContext.Request.QueryString.Value);
                }
            }

            // 执行结果
            var resultContext = await next();

            var exception = resultContext.Exception;
            if (exception != null && !exception.IsNullOrEmpty())
            {
                log.LogStatus = OperateStatusEnum.Fail;
                log.ExecuteResult = $"{exception.Message}。{exception.StackTrace}";
            }
            else
            {
                if (resultContext.Result is ObjectResult result)
                {
                    log.ExecuteResult = JsonConvert.SerializeObject(result.Value);
                    log.LogStatus = OperateStatusEnum.Success;
                }
            }

            await db.Insertable(log).ExecuteCommandAsync();
        }

        private BaseLog InitBaseLog(string msg)
        {
            var context = _httpContextAccessor.HttpContext;
            var requestMethod = context.Request.Method.ToUpper();

            var log = new BaseLog
            {
                RequestMethod = requestMethod,
                OperUrl = context.Request.Path,
                OperAgent = NetHelper.UserAgent,
                OperOs = NetHelper.GetOSVersion(),
                OperBrowser = NetHelper.Browser,
                OperLocation = "",
                OperIp = NetHelper.Ip,
                Remark = msg,
                ExecuteResult = "",
                ExecuteParam = "",
                LogStatus = OperateStatusEnum.Fail,
                CreatorId = UserId,
                CreatorRealName = User?.Account
            };

            return log;
        }
        #endregion
    }
}